package com.ali.sso.common.controller;

import java.net.URI;
import java.net.URISyntaxException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.oltu.oauth2.common.message.types.ParameterStyle;
import org.apache.oltu.oauth2.common.message.types.ResponseType;
import org.apache.oltu.oauth2.common.utils.OAuthUtils;
import org.apache.oltu.oauth2.rs.request.OAuthAccessResourceRequest;
import org.apache.oltu.oauth2.rs.response.OAuthRSResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.ali.sso.common.SSOConstants;
import com.ali.sso.common.model.AccessTokenEntity;
import com.ali.sso.common.model.AuthorizationCodeEntity;
import com.ali.sso.common.model.OauthClient;
import com.ali.sso.common.service.IOAuthService;

@RequestMapping(value="/oauth")
@SuppressWarnings({"unchecked", "rawtypes"})
public class OAuthController {
	static Log logger = LogFactory.getLog(OAuthController.class);
	
	private IOAuthService oauthService;
	
	/**
	 * http://login.alisso.com:8080/ssoserver/oauth/authorize?client_id=app1&response_type=code&redirect_uri=http://app1.alisso.com:8081/app1/login
	 * http://login.alisso.com:8080/ssoserver/oauth/authorize?client_id=app2&response_type=code&redirect_uri=http://app2.alisso.com:8082/app2/login
	 * @param request
	 * @param response
	 * @param model
	 * @return
	 * @throws URISyntaxException
	 * @throws OAuthSystemException
	 */
	@RequestMapping("/authorize")
    public Object authorize(HttpServletRequest request, HttpServletResponse response, Model model) throws URISyntaxException, OAuthSystemException {
        try {
        	//构建OAuth授权请求
			OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);
			
			OauthClient oauthClient = oauthService.findOauthClient(oauthRequest.getClientId());
			//检查传入的客户端id是否正确
            if (null == oauthClient) {
                OAuthResponse oauthResponse = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                    .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                    .setErrorDescription(SSOConstants.INVALID_CLIENT_DESCRIPTION)
                    .buildJSONMessage();
                return new ResponseEntity(oauthResponse.getBody(), HttpStatus.valueOf(oauthResponse.getResponseStatus()));
            }
            
            Subject subject = SecurityUtils.getSubject();
            //如果用户没有登录，跳转到登陆页面
            if(!subject.isAuthenticated()) {
            	//登录失败时跳转到登陆页面
                if(!login(subject, request)) {
                    model.addAttribute("client", oauthClient);
                    return "login";
                }
            }
            
            String username = (String)subject.getPrincipal();
            //生成授权码
            String authorizationCode = null;
            //responseType目前仅支持CODE，另外还有TOKEN
            String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
            if (responseType.equals(ResponseType.CODE.toString())) {
                OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
                authorizationCode = oauthIssuerImpl.authorizationCode();
                
                oauthService.saveAuthorizationCode(username, authorizationCode);
            }
            
            //进行OAuth响应构建
            OAuthASResponse.OAuthAuthorizationResponseBuilder builder = OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);
            //设置授权码
            builder.setCode(authorizationCode);
            //得到到客户端重定向地址
            String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);
            
            //构建响应
            final OAuthResponse oauthResponse = builder.location(redirectURI).buildQueryMessage();

            //根据OAuthResponse返回ResponseEntity响应
            HttpHeaders headers = new HttpHeaders();
            headers.setLocation(new URI(oauthResponse.getLocationUri()));
            return new ResponseEntity(headers, HttpStatus.valueOf(oauthResponse.getResponseStatus()));
		} catch (OAuthProblemException e) {
			//出错处理
            String redirectUri = e.getRedirectUri();
            if (OAuthUtils.isEmpty(redirectUri)) {
                //告诉客户端没有传入redirectUri直接报错
                return new ResponseEntity("OAuth callback url needs to be provided by client!!!", HttpStatus.NOT_FOUND);
            }

            //返回错误消息（如?error=）
            final OAuthResponse oauthResponse = OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
            		.error(e).location(redirectUri).buildQueryMessage();
            HttpHeaders headers = new HttpHeaders();
            headers.setLocation(new URI(oauthResponse.getLocationUri()));
            return new ResponseEntity(headers, HttpStatus.valueOf(oauthResponse.getResponseStatus()));
		}
	}
	
	/**
	 * http://login.alisso.com:8080/ssoserver/oauth/token?client_id=app1&client_secret=cms&grant_type=authorization_code&code=c8d8c3ec2b3f25e630f5b70139ce12b9&redirect_uri=http://app1.alisso.com:8081/app1/login
	 * http://login.alisso.com:8080/ssoserver/oauth/token?client_id=app2&client_secret=cms&grant_type=authorization_code&code=c8d8c3ec2b3f25e630f5b70139ce12b9&redirect_uri=http://app2.alisso.com:8082/app2/login
	 * @param request
	 * @return
	 * @throws URISyntaxException
	 * @throws OAuthSystemException
	 */
	@RequestMapping("/token")
    public HttpEntity token(HttpServletRequest request) throws URISyntaxException, OAuthSystemException {
		try {
			//构建OAuth请求
	        OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);
	
	        OauthClient oauthClient = oauthService.findOauthClient(oauthRequest.getClientId());
	        //检查提交的客户端id是否正确
	        if (null == oauthClient) {
	            OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
	                .setError(OAuthError.TokenResponse.INVALID_CLIENT)
	                .setErrorDescription(SSOConstants.INVALID_CLIENT_DESCRIPTION)
	                .buildJSONMessage();
	            return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
	        }
	        
	        //检查客户端安全KEY是否正确
            if (!oauthClient.getClientSecret().equals(oauthRequest.getClientSecret())) {
                OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                    .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
                    .setErrorDescription(SSOConstants.INVALID_CLIENT_DESCRIPTION)
                    .buildJSONMessage();
                return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
            }
	        
            String authCode = oauthRequest.getParam(OAuth.OAUTH_CODE);
            //检查验证类型，此处只检查AUTHORIZATION_CODE类型，其他的还有PASSWORD或REFRESH_TOKEN
            if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())) {
            		AuthorizationCodeEntity authorizationCode = oauthService.findAuthorizationCode(authCode);
                if (!authorizationCode.getAuthorizationCode().equals(authCode)) {
                    OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                        .setError(OAuthError.TokenResponse.INVALID_GRANT)
                        .setErrorDescription(SSOConstants.INVALID_CODE_DESCRIPTION)
                        .buildJSONMessage();
                    return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
                }
            }
            
            //生成Access Token
            OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
            final String accessToken = oauthIssuerImpl.accessToken();
            AuthorizationCodeEntity authorizationCode = oauthService.findAuthorizationCode(authCode);
            oauthService.saveAccessToken(authorizationCode.getUserLoginId(), accessToken);
            
            //生成OAuth响应
            OAuthResponse response = OAuthASResponse.tokenResponse(HttpServletResponse.SC_OK)
                .setAccessToken(accessToken)
                //.setExpiresIn(String.valueOf(oauthClient.getExpireIn()))
                .setExpiresIn(String.valueOf(3600))
                .buildJSONMessage();

            //根据OAuthResponse生成ResponseEntity
            return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
		} catch (OAuthProblemException e) {
			//构建错误响应
            OAuthResponse res = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST).error(e).buildJSONMessage();
            return new ResponseEntity(res.getBody(), HttpStatus.valueOf(res.getResponseStatus()));
		}
	}
	
	@RequestMapping("/userinfo")
    public HttpEntity userInfo(HttpServletRequest request) throws OAuthSystemException {
        try {

            //构建OAuth资源请求
            OAuthAccessResourceRequest oauthRequest = new OAuthAccessResourceRequest(request, ParameterStyle.QUERY);
            //获取Access Token
            String accessToken = oauthRequest.getAccessToken();

            AccessTokenEntity at = oauthService.findAccessToken(accessToken);
            //验证Access Token
            if (!at.getAccessToken().equals(accessToken)) {
                // 如果不存在/过期了，返回未验证错误，需重新验证
                OAuthResponse oauthResponse = OAuthRSResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                    .setRealm("test")
                    .setError(OAuthError.ResourceResponse.INVALID_TOKEN)
                    .buildHeaderMessage();

                HttpHeaders headers = new HttpHeaders();
                headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
                return new ResponseEntity(headers, HttpStatus.UNAUTHORIZED);
            }
            //返回用户名
            String username = at.getUserLoginId();
            return new ResponseEntity(username, HttpStatus.OK);
        } catch (OAuthProblemException e) {
            //检查是否设置了错误码
            String errorCode = e.getError();
            if (OAuthUtils.isEmpty(errorCode)) {
                OAuthResponse oauthResponse = OAuthRSResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                    .setRealm("test")
                    .buildHeaderMessage();

                HttpHeaders headers = new HttpHeaders();
                headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
                return new ResponseEntity(headers, HttpStatus.UNAUTHORIZED);
            }

            OAuthResponse oauthResponse = OAuthRSResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                .setRealm("test")
                .setError(e.getError())
                .setErrorDescription(e.getDescription())
                .setErrorUri(e.getUri())
                .buildHeaderMessage();

            HttpHeaders headers = new HttpHeaders();
            headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
            return new ResponseEntity(HttpStatus.BAD_REQUEST);
        }
    }
	
	private boolean login(Subject subject, HttpServletRequest request) {
        if("get".equalsIgnoreCase(request.getMethod())) {
            return false;
        }
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            return false;
        }

        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            subject.login(token);
            return true;
        } catch (Exception e) {
            request.setAttribute("error", "登录失败:" + e.getClass().getName());
            return false;
        }
	}

	public IOAuthService getOauthService() {
		return oauthService;
	}
	public void setOauthService(IOAuthService oauthService) {
		this.oauthService = oauthService;
	}
	
}
